package com.citro.agents;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;

/**
 * LLM Client for behavior analysis
 * Supports OpenAI-compatible APIs (OpenAI, DeepSeek, Ollama)
 */
public class LLMClient implements Serializable {

    private static final Logger LOG = LoggerFactory.getLogger(LLMClient.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private final String provider;
    private final String apiKey;
    private final String apiUrl;
    private final String model;
    private final int timeout;

    public LLMClient(String provider, String apiKey, String apiUrl, String model, int timeout) {
        this.provider = provider;
        this.apiKey = apiKey;
        this.apiUrl = apiUrl;
        this.model = model;
        this.timeout = timeout;
    }

    /**
     * Analyze behavior event using LLM
     */
    public BehaviorAnalysisResult analyzeEvent(BehaviorEvent event, String promptTemplate) {
        try {
            // Build prompt
            String prompt = buildPrompt(event, promptTemplate);

            // Call LLM
            String response = callLLM(prompt);

            // Parse response
            return parseResponse(event, response);

        } catch (Exception e) {
            LOG.error("LLM analysis failed: {}", e.getMessage(), e);

            // Fallback to rule-based analysis
            return fallbackAnalysis(event);
        }
    }

    /**
     * Build prompt from event data
     */
    private String buildPrompt(BehaviorEvent event, String template) {
        String eventType = event.getEventType() != null ? event.getEventType() : "unknown";
        String url = event.getUrl() != null ? event.getUrl() : "";
        String title = event.getTitle() != null ? event.getTitle() : "";

        // Get semantic tree from globalStructure
        String semanticTree = "";
        if (event.getGlobalStructure() != null && event.getGlobalStructure().containsKey("semanticTree")) {
            semanticTree = String.valueOf(event.getGlobalStructure().get("semanticTree"));
            // Limit length
            if (semanticTree.length() > 2000) {
                semanticTree = semanticTree.substring(0, 2000);
            }
        }

        // Simple template replacement
        String prompt = template
                .replace("{action}", eventType)
                .replace("{time}", String.valueOf(event.getTimestamp()))
                .replace("{url}", url)
                .replace("{title}", title)
                .replace("{semantic_tree}", semanticTree.isEmpty() ? "(empty)" : semanticTree);

        return prompt;
    }

    /**
     * Call LLM API (OpenAI-compatible)
     */
    private String callLLM(String prompt) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(timeout, TimeUnit.SECONDS)
                .writeTimeout(timeout, TimeUnit.SECONDS)
                .readTimeout(timeout, TimeUnit.SECONDS)
                .build();

        // Build request body
        String requestBody = String.format(
                "{\"model\":\"%s\",\"messages\":[{\"role\":\"user\",\"content\":%s}],\"temperature\":0.3,\"max_tokens\":500}",
                model,
                objectMapper.writeValueAsString(prompt)
        );

        Request.Builder requestBuilder = new Request.Builder()
                .url(apiUrl)
                .post(RequestBody.create(requestBody, MediaType.parse("application/json")));

        // Add Authorization header (skip for Ollama)
        if (!"ollama".equals(provider) && apiKey != null && !apiKey.isEmpty()) {
            requestBuilder.header("Authorization", "Bearer " + apiKey);
        }

        Request request = requestBuilder.build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("LLM API call failed: " + response.code());
            }

            String responseBody = response.body().string();
            JsonNode jsonResponse = objectMapper.readTree(responseBody);

            // Extract content from OpenAI-compatible response
            return jsonResponse
                    .path("choices")
                    .path(0)
                    .path("message")
                    .path("content")
                    .asText();
        }
    }

    /**
     * Parse LLM response (JSON format expected)
     */
    private BehaviorAnalysisResult parseResponse(BehaviorEvent event, String response) {
        try {
            // Remove markdown code blocks if present
            String cleanedResponse = response.trim();
            if (cleanedResponse.contains("```json")) {
                cleanedResponse = cleanedResponse.split("```json")[1].split("```")[0].trim();
            } else if (cleanedResponse.contains("```")) {
                cleanedResponse = cleanedResponse.split("```")[1].split("```")[0].trim();
            }

            // Parse JSON
            JsonNode result = objectMapper.readTree(cleanedResponse);

            return BehaviorAnalysisResult.builder()
                    .userId(event.getUserId())
                    .sessionId(event.getSessionId())
                    .contextId(event.getContextId())
                    .eventType(event.getEventType())
                    .timestamp(event.getTimestamp())
                    .url(event.getUrl())
                    .title(event.getTitle())
                    .behaviorType(result.path("behavior_type").asText("interaction"))
                    .behaviorDescription(result.path("behavior_description").asText(""))
                    .confidence(result.path("confidence").asDouble(0.9))
                    .analysisMethod("llm_" + provider)
                    .build();

        } catch (Exception e) {
            LOG.warn("Failed to parse LLM JSON response, using fallback: {}", e.getMessage());

            // Fallback: use raw text as description
            return BehaviorAnalysisResult.builder()
                    .userId(event.getUserId())
                    .sessionId(event.getSessionId())
                    .contextId(event.getContextId())
                    .eventType(event.getEventType())
                    .timestamp(event.getTimestamp())
                    .url(event.getUrl())
                    .title(event.getTitle())
                    .behaviorType(inferBehaviorType(response))
                    .behaviorDescription(response.trim())
                    .confidence(0.7)
                    .analysisMethod("llm_" + provider + "_fallback")
                    .build();
        }
    }

    /**
     * Infer behavior type from text description
     */
    private String inferBehaviorType(String description) {
        String lower = description.toLowerCase();

        if (lower.contains("search") || lower.contains("query")) {
            return "search";
        } else if (lower.contains("click") || lower.contains("button")) {
            return "interaction";
        } else if (lower.contains("scroll") || lower.contains("browse")) {
            return "navigation";
        } else if (lower.contains("submit") || lower.contains("post")) {
            return "content_creation";
        } else {
            return "interaction";
        }
    }

    /**
     * Fallback rule-based analysis when LLM fails
     */
    private BehaviorAnalysisResult fallbackAnalysis(BehaviorEvent event) {
        String eventType = event.getEventType() != null ? event.getEventType() : "unknown";
        String behaviorType = "interaction";
        String description = "Rule-based fallback: " + eventType + " event";

        // Simple rule-based mapping
        switch (eventType.toLowerCase()) {
            case "click":
                behaviorType = "interaction";
                description = "User clicked on an element";
                break;
            case "keydown":
            case "input":
                behaviorType = "content_creation";
                description = "User inputted text";
                break;
            case "scroll":
                behaviorType = "navigation";
                description = "User scrolled the page";
                break;
            case "submit":
                behaviorType = "content_creation";
                description = "User submitted a form";
                break;
            default:
                behaviorType = "interaction";
                description = "User performed: " + eventType;
        }

        return BehaviorAnalysisResult.builder()
                .userId(event.getUserId())
                .sessionId(event.getSessionId())
                .contextId(event.getContextId())
                .eventType(event.getEventType())
                .timestamp(event.getTimestamp())
                .url(event.getUrl())
                .title(event.getTitle())
                .behaviorType(behaviorType)
                .behaviorDescription(description)
                .confidence(0.6)
                .analysisMethod("rule_based")
                .build();
    }
}
